package org.gpp.proj1.communication.processor;

import static org.easymock.EasyMock.createMock;
import static org.easymock.EasyMock.eq;
import static org.easymock.EasyMock.expectLastCall;
import static org.easymock.EasyMock.replay;
import static org.easymock.EasyMock.reset;
import static org.easymock.EasyMock.verify;

import java.rmi.NotBoundException;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;

import junit.framework.TestCase;

import org.gpp.proj1.communication.CommunicationException;
import org.gpp.proj1.communication.processor.test.RequestCounter;
import org.gpp.proj1.communication.protocol.Message;
import org.gpp.proj1.communication.protocol.ProtocolIdentifiers;
import org.gpp.proj1.communication.sender.manager.RequestSenderManagerIF;

/**
 * Unit tests for the RMIRequestProcessor class
 * 
 * @author Paolo Victor, paolovictor@gmail.com
 */
public class RMIRequestProcessorTest extends TestCase {
	
	private static final int TEST_PORT = 1099;
	private static final String FAKE_SERVER_ID = "FAKEID";

	public void testStartProcessor() throws RemoteException, CommunicationException {
		RequestSenderManagerIF manager = createMock( RequestSenderManagerIF.class );
		
		RMIRequestProcessor processor = new RMIRequestProcessor( manager, TEST_PORT );
		processor.startup();
				
		// Checking if processor is bound
		try {
			RequestProcessorIF proc = (RequestProcessorIF) LocateRegistry.getRegistry( TEST_PORT ).lookup( RMIRequestProcessor.REQUEST_PROCESSOR );
			proc.ping();
		} catch (NotBoundException e) {
			fail();
		}
		
		try {
			processor.startup(); // should fail!
			fail();
		} catch( CommunicationException e ) {			
		}
		
		// Checking if processor is bound
		try {
			RequestProcessorIF proc = (RequestProcessorIF) LocateRegistry.getRegistry( TEST_PORT ).lookup( RMIRequestProcessor.REQUEST_PROCESSOR );
			proc.ping();
		} catch (NotBoundException e) {
			fail();
		}
		
		// Unbinding processor
		try {
			LocateRegistry.getRegistry( TEST_PORT ).unbind( RMIRequestProcessor.REQUEST_PROCESSOR );
		} catch (NotBoundException e) {
			fail();
		}
	}
	
	public void testSendRequest() throws RemoteException, CommunicationException {
		RequestSenderManagerIF managerMock = createMock( RequestSenderManagerIF.class );
		RequestSenderManagerIF managerMock2 = createMock( RequestSenderManagerIF.class );
		
		// Starting request processors
		RMIRequestProcessor processor = new RMIRequestProcessor( managerMock, TEST_PORT );
		processor.startup();
		
		// This proc must be online because the server will try to contact it
		RMIRequestProcessor processor2 = new RMIRequestProcessor( managerMock2, TEST_PORT + 1 );
		processor2.startup();		
		
		// Getting bound processors
		RequestProcessorIF proc = null;
		RequestProcessorIF procClient = null;
		try {
			proc = (RequestProcessorIF) LocateRegistry.getRegistry( TEST_PORT ).lookup( RMIRequestProcessor.REQUEST_PROCESSOR );
			procClient = (RequestProcessorIF) LocateRegistry.getRegistry( TEST_PORT + 1 ).lookup( RMIRequestProcessor.REQUEST_PROCESSOR );
		} catch (NotBoundException e) {
			fail();
		}
		
		// Creating and registeringfake request listener
		RequestCounter requestCounter = new RequestCounter();
		processor.registerListener( requestCounter  );
		
		// Connecting to the server processor
		String clientId = proc.connect( FAKE_SERVER_ID, TEST_PORT + 1 );
		
		// Recording mock - it must confirm the client id
		reset( managerMock );		
		managerMock.contains( eq( clientId ) ); expectLastCall().andReturn( true );
		replay( managerMock );
		
		// Sending fake message
		Message preparedMessage = new Message( ProtocolIdentifiers.CHAT, new String[]{} );
		preparedMessage.setSenderId( clientId );
		
		proc.receive( preparedMessage );
		
		// Verifying mocks
		verify( managerMock );
		
		// Checking if listener was notified
		assertEquals( 1, requestCounter.received.size() ) ;
		assertEquals( ProtocolIdentifiers.CHAT, requestCounter.received.get(0).getIdentifier() );
		assertEquals( clientId, requestCounter.received.get(0).getSenderId() );
		
		// Now, processor2 will try to send a message
		RequestCounter requestCounter2 = new RequestCounter();
		processor2.registerListener( requestCounter2  );
		
		// Recording mock - it must confirm the server id
		reset( managerMock2 );		
		managerMock2.contains( eq( FAKE_SERVER_ID ) ); expectLastCall().andReturn( true );
		replay( managerMock2 );
		
		// Sending fake message
		preparedMessage = new Message( ProtocolIdentifiers.CLIENTOFFLINE, new String[]{} );
		preparedMessage.setSenderId( FAKE_SERVER_ID );
		
		procClient.receive( preparedMessage );
		
		// Verifying mocks
		verify( managerMock );
		
		// Checking if listener was notified
		assertEquals( 1, requestCounter2.received.size() ) ;
		assertEquals( ProtocolIdentifiers.CLIENTOFFLINE, requestCounter2.received.get(0).getIdentifier() );
		assertEquals( FAKE_SERVER_ID, requestCounter2.received.get(0).getSenderId() );
		
		// Unbinding processors
		try {
			LocateRegistry.getRegistry( TEST_PORT ).unbind( RMIRequestProcessor.REQUEST_PROCESSOR );
			LocateRegistry.getRegistry( TEST_PORT + 1).unbind( RMIRequestProcessor.REQUEST_PROCESSOR );
		} catch (NotBoundException e) {
			fail();
		}
	}
	
	public void testStopProcessor() throws RemoteException, CommunicationException {
		RequestSenderManagerIF manager = createMock( RequestSenderManagerIF.class );
		
		RMIRequestProcessor processor = new RMIRequestProcessor( manager, TEST_PORT );
		processor.startup();
				
		// Checking if processor is bound
		try {
			RequestProcessorIF proc = (RequestProcessorIF) LocateRegistry.getRegistry( TEST_PORT ).lookup( RMIRequestProcessor.REQUEST_PROCESSOR );
			proc.ping();
		} catch (NotBoundException e) {
			fail();
		}
				
		// Checking if processor is bound
		try {
			RequestProcessorIF proc = (RequestProcessorIF) LocateRegistry.getRegistry( TEST_PORT ).lookup( RMIRequestProcessor.REQUEST_PROCESSOR );
			proc.ping();
		} catch (NotBoundException e) {
			fail();
		}
		
		// Recording mock
		reset( manager );
		// The manager must be notified about the disconnection
		manager.notifyDisconnected(); expectLastCall().once();
		replay( manager );
		
		processor.stop();
		
		verify( manager );
		
		// Unbinding processor
		try {
			LocateRegistry.getRegistry( TEST_PORT ).unbind( RMIRequestProcessor.REQUEST_PROCESSOR );
			fail();
		} catch (NotBoundException e) {
			// Should fail! The processor is unbound
		}
	}
}
